﻿using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using MyCompany.MyProject.Common;
using MyCompany.MyProject.Common.DB;
using MyCompany.MyProject.Extensions.Auth;
using MyCompany.MyProject.IService;
using MyCompany.MyProject.Service;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

namespace MyCompany.MyProject.WebApi.Controllers
{
    [ApiController]
    [Route("api/auth")]
    public class AuthController : ControllerBase
    {
        private readonly IJwtService _jwtService;
        private readonly IUserService _userService;
        private readonly PermissionRequirement _requirement;

        public AuthController(IJwtService jwtService, IUserService userService, PermissionRequirement requirement)
        {
            _jwtService = jwtService;
            _userService = userService;
            _requirement = requirement;
        }
        private string MD5Encrypt32(string password = "")
        {
            string pwd = string.Empty;
            try
            {
                if (!string.IsNullOrWhiteSpace(password))
                {
                    // 实例化 MD5 对象
                    MD5 md5 = MD5.Create();
                    // 加密后是一个字符型的数组，这里需要注意编码 UTF8/Unicode 等
                    byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
                    // 通过使用循环，将字节类型的数组转化为字符串，此字符串是常规字符格式化所得
                    foreach (var b in s)
                    {
                        // 将得到的字符串使用十六进制类型格式，格式后的字符是小写的字母，如果使用大写（X）则格式后的字符是大写字符 
                        pwd = string.Concat(pwd, b.ToString("X2"));
                    }
                }
            }
            catch
            {
                throw new Exception($"错误的 password 字符串：【{password}】");
            }
            return pwd;
        }

        [HttpPost("login")]
        public async Task<IActionResult> Login([FromBody] LoginRequest request)
        {
            request.Password = MD5Encrypt32(request.Password);

            var user = await _userService.Query(d => d.LoginName == request.UserName && d.LoginPWD == request.Password && d.IsDeleted == false);
            if (user == null)
                return Unauthorized();
            if (user.Count > 0)
            {
                var userRoles = await _userService.GetUserRoleNameStr(request.UserName, request.Password);
                var u = user.FirstOrDefault();
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name,request.UserName),
                    new Claim(JwtRegisteredClaimNames.Jti,user.FirstOrDefault().Id.ToString()),
                    new Claim("TenantId",user.FirstOrDefault().TenantId.ToString()),
                    new Claim(JwtRegisteredClaimNames.Iat,DateTime.Now.DateToTimeStamp()),
                    new Claim(ClaimTypes.Expiration,DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
                };

                claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));
                var tokens = GenerateTokens(claims.ToArray(), _requirement);

                return Ok(tokens);
            }
            return NoContent();
        }

        [HttpPost("refresh")]
        public async Task<IActionResult> Refresh([FromBody] RefreshTokenRequest request)
        {
            if (!ValidateRefreshToken(request.RefreshToken))
                return Unauthorized();

            var userId = GetUserIdFromExpiredToken(request.AccessToken);
            if (string.IsNullOrEmpty(userId))
                return Unauthorized();

            var user = await _userService.QueryById(userId);
            if (user == null)
                return Unauthorized();

            var userRoles = await _userService.GetUserRoleNameStr(user.LoginName, user.LoginPWD);
            // 如果是基于用户的授权策略，这里要添加用户；如果是基于角色的授权策略，这里要添加角色
            var claims = new List<Claim>
                        {
                            new Claim(ClaimTypes.Name,user.LoginName),
                            //new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ObjToString()),
                            new Claim(JwtRegisteredClaimNames.Iat,DateTime.Now.DateToTimeStamp()),
                            new  Claim(ClaimTypes.Expiration,DateTime.Now.AddSeconds(_requirement.Expiration.TotalSeconds).ToString())
                        };
            claims.AddRange(userRoles.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));

            // 用户标记
            var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
            identity.AddClaims(claims);
            var tokens = _jwtService.GenerateTokens(user.Id.ToString(), user.LoginName);

            return Ok(tokens);
        }

        [HttpPost("logout")]
        [Authorize]
        public IActionResult Logout()
        {
            // 在实际应用中，你可能需要将token加入黑名单
            return Ok();
        }

        private TokenResponse GenerateTokens(Claim[] claims,PermissionRequirement permissionRequirement)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            //var key = Encoding.ASCII.GetBytes("sdfsdfsrty45634kkhllghtdgdfss345t678fs");
            var now = DateTime.Now;
            var singingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("sdfsdfsrty45634kkhllghtdgdfss345t678fs"));
            // Access token (short-lived)
            //var accessTokenDescriptor = new SecurityTokenDescriptor
            //{
            //    //Subject = new ClaimsIdentity(new[]
            //    //{
            //    //    new Claim(ClaimTypes.NameIdentifier, userId),
            //    //    new Claim(ClaimTypes.Name, username)
            //    //}),
            //    Expires = DateTime.UtcNow.AddSeconds(30), // 15分钟过期
            //    SigningCredentials = new SigningCredentials(
            //        new SymmetricSecurityKey(key),
            //        SecurityAlgorithms.HmacSha256Signature),
            //    Issuer = "Blog.Core",
            //    Audience = "wr",
            //    NotBefore = DateTime.Now,
            //    Claims =claims.ToList()
            //};
            // 实例化 JwtSecurityToken
            var accessToken = new JwtSecurityToken(
                issuer: "Blog.Core",
                audience: "wr",
                claims: claims,
                notBefore: now,
                expires: now.Add(permissionRequirement.Expiration),
                signingCredentials: new SigningCredentials(singingKey, SecurityAlgorithms.HmacSha256));

            //var accessToken = tokenHandler.CreateToken(accessTokenDescriptor);

            //// Refresh token (long-lived)
            //var refreshTokenDescriptor = new SecurityTokenDescriptor
            //{
            //    Subject = new ClaimsIdentity(new[]
            //    {
            //        new Claim(ClaimTypes.NameIdentifier, permissionRequirement. userId)
            //    }),
            //    Expires = DateTime.UtcNow.AddMinutes(1), // 7天过期
            //    SigningCredentials = new SigningCredentials(
            //        new SymmetricSecurityKey(key),
            //        SecurityAlgorithms.HmacSha256Signature)
            //};

            //var refreshToken = tokenHandler.CreateToken(refreshTokenDescriptor);

            var refreshToken = new JwtSecurityToken(
              issuer: "Blog.Core",
              audience: "wr",
              claims: claims,
              notBefore: now,
              expires: DateTime.UtcNow.AddMinutes(1),
              signingCredentials: new SigningCredentials(singingKey, SecurityAlgorithms.HmacSha256));


            return new TokenResponse
            {
                AccessToken = tokenHandler.WriteToken(accessToken),
                RefreshToken = tokenHandler.WriteToken(refreshToken),
                AccessTokenExpiry = now.Add(permissionRequirement.Expiration)
            };
        }

        private bool ValidateRefreshToken(string refreshToken)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes("sdfsdfsrty45634kkhllghtdgdfss345t678fs");

            var symmetricKeyAsBase64 = AppSecretConfig.Audience_Secret_String;
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var Issuer = AppSettings.app(new string[] { "TokenParms", "Issuer" });
            var Audience = AppSettings.app(new string[] { "TokenParms", "Audience" });
            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);

            // 令牌验证参数
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,
                ValidateIssuer = true,
                ValidIssuer = Issuer,
                ValidateAudience = true,
                ValidAudience = Audience,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromSeconds(30),
                RequireExpirationTime = true,
            };

            try
            {
                tokenHandler.ValidateToken(refreshToken, tokenValidationParameters, out _);

                return true;
            }
            catch
            {
                return false;
            }
        }

        private string GetUserIdFromExpiredToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes("sdfsdfsrty45634kkhllghtdgdfss345t678fs");

            var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateLifetime = false, // 故意不验证过期时间
                ClockSkew = TimeSpan.Zero
            }, out _);

            return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        }
    }

    public class LoginRequest
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }

    public class RefreshTokenRequest
    {
        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }
    }

    public class TokenResponse
    {
        public string AccessToken { get; set; }
        public string RefreshToken { get; set; }
        public DateTime AccessTokenExpiry { get; set; }
    }
}
